﻿using DevExpress.DataAccess.ObjectBinding;
using DevExpress.Utils.Serializing;
using DevExpress.XtraBars.Docking;
using DevExpress.XtraBars.Docking2010.Views;
using DevExpress.XtraEditors;
using DevExpress.XtraPrinting;
using DevExpress.XtraPrinting.Preview;
using DevExpress.XtraReports.Extensions;
using DevExpress.XtraReports.UI;
using DevExpress.XtraReports.UserDesigner;
using System;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using XCI.Helper;
using XCI.Windows;

namespace XCI.Core
{
    /// <summary>
    /// 报表对象
    /// </summary>
    public class ReportObject
    {
        private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
        public XtraReportx Report { get; private set; }
        public ServerFile SettingFile { get; private set; }
        public LoadingMask LoadingPanel { get; private set; }
        public string Code { get; private set; }
        public string Name { get; private set; }

        static ReportObject()
        {
            string propertiesPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ReportFavoriteProperties.xml");
            FavoritePropertyExtension.RegisterExtensionGlobal(new FavoritePropertyDirectoryExtension(propertiesPath));

            string galleryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ReportGallery.xml");
            ReportGalleryExtension.RegisterExtensionGlobal(new ReportGalleryDirectoryExtension(galleryPath));
        }

        /// <summary>
        /// 初始化报表对象
        /// </summary>
        /// <param name="code">编码</param>
        /// <param name="name">名称</param>
        public ReportObject(string code, string name)
        {
            if (string.IsNullOrEmpty(code)) throw new ApplicationException("请指定报表编码");
            this.Code = code;
            this.Name = name;
        }

        /// <summary>
        /// 加载报表对象
        /// </summary>
        /// <param name="loadMaskParent">加载面板容器</param>
        public async Task LoadAsync(Control loadMaskParent = null)
        {
            if (loadMaskParent != null) LoadingPanel = new LoadingMask(loadMaskParent);

            try
            {
                LoadingPanel?.Show();
                SettingFile = CrreateServerFile(Code);
                await SettingFile.DownloadAsync();
                if (File.Exists(SettingFile.LocalFilePath))
                {
                    try
                    {
                        if (XtraReport.FromFile(SettingFile.LocalFilePath) is XtraReportx r)
                        {
                            Report = r;
                        }
                    }
                    catch (Exception e)
                    {
                        LoadingPanel?.Hide();
                        Logger.Error(e, "加载Report配置时出错");
                    }
                }

                if (Report == null)
                {
                    Report = new XtraReportx
                    {
                        Font = new Font("宋体", 10f),
                        PaperKind = PaperKind.A4,
                        Landscape = false,
                        RollPaper = false,
                        ReportUnit = ReportUnit.TenthsOfAMillimeter,
                        Margins = new Margins(100, 100, 100, 100)
                    };
                }
            }
            finally
            {
                LoadingPanel?.Hide();
            }
        }

        /// <summary>
        /// 创建服务器数据文件
        /// </summary>
        /// <param name="code">对象编码</param>
        public ServerFile CrreateServerFile(string code)
        {
            string localCatalog = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDefaultSettings.ReportStoreLocalRootCatalog);
            string serverCatalog = $"/{AppDefaultSettings.ReportStoreServerRootCatalog}";
            return new ServerFile(localCatalog, serverCatalog, $"{code}.xml");
        }

        /// <summary>
        /// 设置数据源
        /// </summary>
        /// <param name="dataSource">数据源对象</param>
        public void SetDataSource(object dataSource)
        {
            if (dataSource is DataTable || dataSource is DataSet)
            {
                Report.DataSource = dataSource;
            }
            else
            {
                Report.DataSource = new ObjectDataSource { Name = "ReportDataSource", DataSource = dataSource };
            }
        }

        /// <summary>
        /// 查看
        /// </summary>
        /// <param name="documentViewer"></param>
        public void View(DocumentViewer documentViewer)
        {
            try
            {
                LoadingPanel?.Show();
                documentViewer.PrintingSystem = Report.PrintingSystem;
                Report.CreateDocument(true);
            }
            finally
            {
                LoadingPanel?.Hide();
            }
        }

        /// <summary>
        /// 打印
        /// </summary>
        public void Print()
        {
            try
            {
                Report.ShowPrintStatusDialog = false;
                Report.ShowPrintMarginsWarning = false;
                Report.PrintingSystem.ShowMarginsWarning = false;
                Report.PrintingSystem.ShowPrintStatusDialog = false;
                Report.Print();
            }
            catch (Exception ex)
            {
                MessageBoxHelper.ShowError("打印报表时出错;" + ex.Message);
            }
        }

        /// <summary>
        /// 预览
        /// </summary>
        public void Preview()
        {
            LoadingPanel?.Show();
            var frmPreview = new FrmReportPreview(this);
            frmPreview.Text = Name + "- 预览";
            frmPreview.Load+=(o,e)=> LoadingPanel?.Hide();
            if (LoadingPanel?.Owner != null)
            {
                frmPreview.Show(LoadingPanel.Owner);
            }
            else
            {
                frmPreview.Show();
            }
        }

        /// <summary>
        /// 设计
        /// </summary>
        /// <param name="saveCallback">保存成功回调函数</param>
        public void Design(Action saveCallback = null)
        {
            try
            {
                //https://docs.devexpress.com/XtraReports/10715/winforms-reporting/end-user-report-designer
                LoadingPanel?.Show();
                Report.PrintingSystem.ExecCommand(PrintingSystemCommand.StopPageBuilding);
                //if (string.IsNullOrEmpty(name)) name = "报表模板";
                Report.DisplayName = Name;

                ReportDesignTool designTool = new ReportDesignToolx(Report);
                IDesignForm designForm = designTool.DesignForm;

                // (designForm as XRDesignForm).SaveState = false;

                if (designForm is XtraForm dform && saveCallback != null)
                {
                    dform.FormClosed += (o, e) => { saveCallback.Invoke(); };
                }

                designTool.DesignForm.DesignMdiController.DesignSettings.DockingViewStyle = DockingViewStyle.Light;
                designForm.DesignMdiController.SetCommandVisibility(ReportCommand.NewReport, DevExpress.XtraReports.UserDesigner.CommandVisibility.None);
                designForm.DesignMdiController.SetCommandVisibility(ReportCommand.NewReportWizard, DevExpress.XtraReports.UserDesigner.CommandVisibility.None);
                designForm.DesignMdiController.SetCommandVisibility(ReportCommand.VerbReportWizard, DevExpress.XtraReports.UserDesigner.CommandVisibility.None);
                designForm.DesignMdiController.SetCommandVisibility(ReportCommand.SaveAll, DevExpress.XtraReports.UserDesigner.CommandVisibility.None);
                designForm.DesignMdiController.SetCommandVisibility(ReportCommand.Close, DevExpress.XtraReports.UserDesigner.CommandVisibility.None);
                designForm.DesignMdiController.SetCommandVisibility(ReportCommand.ShowTabbedInterface, DevExpress.XtraReports.UserDesigner.CommandVisibility.None);

                designForm.DesignMdiController.DesignPanelLoaded += (ox, ex) =>
                {
                    XRDesignPanel panel = (XRDesignPanel)ox;
                    panel.Text = Name;
                    panel.FileName = SettingFile.LocalFilePath;
                    panel.AddCommandHandler(new SaveCommandHandler(panel, SettingFile.LocalFilePath, async () =>
                        {
                            await SettingFile.UploadAsync();
                        }));
                };

                PropertyGridDockPanel propertyGrid = (PropertyGridDockPanel)designForm.DesignDockManager[DesignDockPanelType.PropertyGrid];
                propertyGrid.Visibility = DockVisibility.Visible;
                propertyGrid.UseTabbedView = false;
                propertyGrid.UseOfficeInspiredView = false;

                FieldListDockPanel fieldList = (FieldListDockPanel)designForm.DesignDockManager[DesignDockPanelType.FieldList];
                fieldList.Visibility = DockVisibility.Visible;

                designTool.ShowDesigner();
            }
            catch (Exception ex)
            {
                LoadingPanel?.Hide();
                MessageBoxHelper.ShowError("设计报表时出错," + ex.Message);
            }
            finally
            {
                LoadingPanel?.Hide();
            }
        }
    }

    public class ReportDesignToolx : ReportDesignTool
    {
        public ReportDesignToolx(XtraReport report) : base(report)
        {
        }

        protected override IDesignForm CreateDesignForm()
        {
            return (IDesignForm)new XRDesignFormx();
        }
    }

    public class XRDesignFormx : XRDesignForm
    {
        protected override FormLayoutSerializer CreateFormLayoutSerializer()
        {
            return (FormLayoutSerializer)new DesignFormLayoutSerializerx((Form)this, nameof(XRDesignForm));
        }
    }

    public class DesignFormLayoutSerializerx : DesignFormLayoutSerializer
    {
        public DesignFormLayoutSerializerx(Form form, string name)
            : base(form, name)
        {
        }

        protected override XtraSerializer CreateSerializer()
        {
            return (XtraSerializer)new XmlXtraSerializer();
        }

        protected override string Path
        {
            get
            {
                var path = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ReportDesignerLayout.xml");
                if (!File.Exists(path))
                {
                    File.WriteAllText(path, "<XtraSerializer version=\"1.0\" application=\"XRDesignForm\"></XtraSerializer>");
                }
                return path;
            }
        }
    }

    public class SaveCommandHandler : DevExpress.XtraReports.UserDesigner.ICommandHandler
    {
        private readonly XRDesignPanel panel;
        private readonly string path;
        private readonly Action saveCallback;

        public SaveCommandHandler(XRDesignPanel panel, string path, Action saveCallback)
        {
            this.panel = panel;
            this.path = path;
            this.saveCallback = saveCallback;
        }

        public void HandleCommand(DevExpress.XtraReports.UserDesigner.ReportCommand command,
            object[] args)
        {
            // Save the report.
            Save();
        }

        public bool CanHandleCommand(DevExpress.XtraReports.UserDesigner.ReportCommand command,
            ref bool useNextHandler)
        {
            useNextHandler = command != ReportCommand.SaveFile;
            return !useNextHandler;
        }

        void Save()
        {
            IOHelper.CreateDirectoryByPath(path);
            panel.Report.SaveLayout(path);
            panel.ReportState = ReportState.Saved;
            saveCallback?.Invoke();
        }
    }
}